package Grafica;

import java.awt.Point;
import java.util.Collection;
import java.util.Hashtable;

/**
 *
 * @author robertoanzaldua
 */
public class Grafica implements java.io.Serializable {
    
    private static final long serialVersionUID = -694442772198448869L;
    /**Un arreglo con los vértices de la gráfica
     */
    private Vertice[]vertices;
    //El número de vértices de la gráfica
    public int numV;
    //Una Hashtable con información para
    private Hashtable ListaDeAdy = new Hashtable();
    //El color con el que se pintarán los vértices
    private String colorV = "1 1 1";

    //Un arreglo de cadenas que define como se ven los vértices
    private String[]vertGV;


    /**Constructor que crea la gráfica en base a un arreglo de vértices
     *
     * @param vertices
     */
    public Grafica(Vertice[]vertices){
        if(vertices!=null){
            this.vertices = vertices;
            numV = vertices.length;
            vertGV = new String[numV];
            for(int i=0;i<vertices.length;i++)
               vertGV[i] = "V"+i+"[shape=ellipse, width=.5, height=.5];\n";
        }
    }

    /**Marca la arista (vi,vj), con el color indicado.
     * La arista será dirigida sí es que lo indicamos en el método.
     *
     * @param i El índice del vértice de salida de la arista
     * @param j El índice del vértice a donde llega
     * @param inf Contiene información relacionada con la arista, primero
     *        debe contener el color que se le va a asignar.
     * @param d Indica si se trata de una arista dirigida
     */
    public void marcaArista(int i, int j, String inf, boolean d){

        Point p = new Point((j>i?i:j),(j>i?j:i));
        ListaDeAdy.put(p,"V"+p.x+" -> "+"V"+p.y+"[color="+inf+","+(d ?
            (" arrow"+ (i==p.x ? "head" :"tail")+"=normal, arrow"+
            (i!=p.x ? "head" :"tail")+"=none")
            : " arrowhead=none")+"];\n");
    }

    /**Marca el vértice i durante toda la ejecución del algoritmo
     * 
     * @param color
     */
    public void marcaVerticePerm(int i, String color){
        vertices[i].marcaVertice(color);
    }

    /**Regresa todas las aristas en el lenguaje de Graphviz
     *
     * @return String aristas en el lenguaje de Graphviz
     */
    private String aristasGV(){
        Collection<String> ad = ListaDeAdy.values();
        StringBuilder grafica = new StringBuilder();
        for(String s : ad)
            grafica.append(s);

        for(int i=0;i<numV;i++)
            for(Vertice v : getV(i).vecinos)
                if(i<v.nV())
                if(!ListaDeAdy.containsKey(new Point(i,v.nV())))
                    grafica.append(generaArista(i, v.nV()));

        return grafica.toString();
    }

    /**Genera la gráfica en el lenguaje de graphviz
     *
     * @return <code>String</code> La cadena que representa a la gráfica
     * en el lenguaje de graphviz
     */
    public String generaGrafica(){
        StringBuffer grafica = new StringBuffer("digraph G {\n");
        for(int i=0;i<numV;i++)
            grafica.append("V"+i+"[color="+getColor(i)
                    +" shape=ellipse, width=.5, height=.5];\n");

        grafica.append(aristasGV());
        grafica.append("center = true;\n}");
        return grafica.toString();
    }

    /**Genera una gráfica marcando los vértices con grado impar
     *
     * @return
     */
    public String generaGraficaGr() {        
        StringBuffer grafica = new StringBuffer("digraph G {\n");

        for(int i=0;i<numV;i++){
            grafica.append("V"+i+" ["+(getV(i).ady().size()%2==1 ?
                "color=\""+getColor(i)+"\",style=filled,":"")
                +"shape=ellipse, width=.7, height=.7];\n");
        }
        grafica.append(aristasGV());
        grafica.append("center = true;\n}");
        return grafica.toString();
    }

     /**Regresa una cadena que representa la gráfica en el lenguaje de GraphViz,
      * con información para generar los vértices.
      *
      * @param inf - Un arreglo con las distancias de los vértices
      * @param p los predecesores o un número que indica en que momento fue
      * descubierto el vértice
      * @return
      */
    public String generaGrafica(int[]d,String[]p){
        //if(marcado==null)
            //marcado = new boolean[raiz.getChildCount()];
        StringBuffer grafica = new StringBuffer("digraph G {\n");
        //size = \"4.3,7.4\";
        grafica.append("center = true;\n");

        for(int i=0;i<numV;i++){
            grafica.append("V"+i+" ["+
                "color=\""+getColor(i)+"\","+" label=\"V"+i+" \\n "
                +(d[i]!=Integer.MAX_VALUE ? d[i] : "oo")+(p!=null ?
                    ", "+p[i]:"")+"\",shape=ellipse, width=.7, height=.7];\n");
        }

        grafica.append(this.aristasGV());
        grafica.append("}");
        return grafica.toString();
    }


    /**Regresa el color asignado al vértice i
     *
     * @param i
     * @return String el color representado con cadena.
     */
    private String getColor(int i){
        return vertices[i].getColor()==null ? colorV : 
            vertices[i].getColor()+",style=filled,";
    }

    /**Genera la arista (x,y) en el lenguaje de Graphviz
     *
     * @param x El índice de algún vértice
     * @param y El índice de algún vértice
     * @return La arista en forma de cadena
     */
    private String generaArista(int x, int y){
        return x<y ? "V"+x + "->" + "V" + y + "[arrowhead=none];\n" : "";
    }

    /**Nos dice si la gráfica tiene aristas
     *
     * @return boolean <code>True</code> si tiene aristas, <code>False</code>
     * si no tiene aristas.
     */
    public boolean tieneAristas(){
        for(Vertice v : vertices)
            if(v.vecinos != null)
                return true;
        
        return false;
    }

    
    /**Genera una cadena con las aristas de G
     * 
     * @return <code>String</code> La cadena que representa a las aristas.
     */
    public String aristas(){
        StringBuilder aristas = new StringBuilder(" ");
        if(vertices!=null)
        for(int i = 0,j=0;i<vertices.length;i++){
            for(Vertice vj : vertices[i].vecinos){
                if(vj.nV()<vertices[i].nV()){
                    if(j%10==9)
                        aristas.append("\n");
                    aristas.append("V"+vertices[i].nV()+"V"+vj.nV()+", ");
                    j++;
                }
            }
        }

       // String ars = aristas.toString().replaceAll("((V0-9{1,}+V0-9{1,}+, ){4})", "$1 \\n");
        //return ars.substring(0,aristas.length()-2);
                return aristas.length() > 0 ? aristas.toString().substring(0,aristas.length()-2) : "";
   }

//V1V0, V2V1, V2V0, V3V0, V3V1, V3V2, V4V1, V4V3, V4V0, V4V3
//

    /**Genera una cadena con las aristas de G
     * @param  com Un complemento para la arista
     * @return <code>String</code> La cadena que representa a las aristas.
     */
    public String aristas(String com){
        StringBuilder aristas = new StringBuilder("");
        for(int i = 0;i<vertices.length;i++)
            for(Vertice vj : vertices[i].vecinos)
                if(vj.nV()<vertices[i].nV())
                    aristas.append("V"+vertices[i].nV()+"V"+vj.nV()+""+com);                
        
        return aristas.toString();
   }

    /**Regresa un arreglo con los vértices de esta gráfica
     *
     * @return Vertice Un arreglo de vértices
     */
    public Vertice[] getVertices(){
        return vertices;
    }


    /**Regresa el primer vértice en la lista de adyacencias de i
     * 
     * @param i - El vértice del que se quiere obtener al disponible.
     * @return El primer vértice disponible.
     */
    public Vertice disp(int i){
        return vertices[i].disp();
    }

    /**Regresa el i-ésimo vértice de esta gráfica
     *
     * @param i el número del vértice
     * @return El vértice a regresar
     */
    public Vertice getV(int i){
        return vertices[i];
    }


    /**Busca alguna arista disponible en las listas de adyacencias, si la
     * encuentra regresa el número asociado al vértice, si no, regresa -1
     *
     * @return int, el entero asociado al vértice o -1 en caso de no encontrar
     * aristas disponibles
     */
    public int existenAristas(){
        for(Vertice v : vertices)
            if(v.disp()!=null)
                return v.disp().nV();
        return -1;
    }

    public void eliminaArista(int i,int j){
        vertices[i].elimina(vertices[j]);
        vertices[j].elimina(vertices[i]);
    }
}
